package com.yanqu.road.server.manager.activity.newyearbag;

import com.yanqu.road.dao.impl.activity.newyearbag.NewYearBagOpenUserLimitDaoImpl;
import com.yanqu.road.entity.activity.ActivityConfig;
import com.yanqu.road.entity.activity.ActivityInfo;
import com.yanqu.road.entity.activity.newyearbag.*;
import com.yanqu.road.entity.config.goods.OpenGoodsBagResult;
import com.yanqu.road.entity.enums.activity.newyearbag.eNewYearBagType;
import com.yanqu.road.entity.player.UserBaseInfo;
import com.yanqu.road.logic.bussiness.activity.NewYearBagBusiness;
import com.yanqu.road.logic.bussiness.servercenter.ActivityBussiness;
import com.yanqu.road.logic.pb.PlayerBasePb;
import com.yanqu.road.message.YanQuMessage;
import com.yanqu.road.pb.activity.NewYearBagProto;
import com.yanqu.road.server.logic.MessageHelper;
import com.yanqu.road.server.manager.user.CrossUserMgr;
import com.yanqu.road.server.manger.TempGoodsBagMgr;
import com.yanqu.road.server.protocol.AgentProtocol;
import com.yanqu.road.server.protocol.ClientProtocol;
import com.yanqu.road.server.protocol.GameProtocol;
import com.yanqu.road.utils.RandomHelper;
import com.yanqu.road.utils.YanQuMessageUtils;
import com.yanqu.road.utils.date.DateHelper;
import com.yanqu.road.utils.property.Property;
import com.yanqu.road.utils.property.PropertyHelper;
import com.yanqu.road.utils.string.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.ConcurrentHashMap;

public class CrossNewYearBagActivity {

    protected static Logger logger = LogManager.getLogger(CrossNewYearBagActivity.class.getName());
    private NewYearBagConfig config;

    private final Object lock = new Object();
    private final Random drawRandom = new Random();
    private NewYearBagPool pool;
    private Map<Long, NewYearBagUserData> userDataMap = new ConcurrentHashMap<>();
    private List<NewYearBagNotice> noticeList = new ArrayList<>();
    private List<NewYearBagNotice> needInsertNoticeList = new ArrayList<>();
    private Map<String, NewYearBagDevice> deviceMap = new ConcurrentHashMap<>();
    private Map<String, NewYearBagOpenUserLimit> openUserLimitDataMap = new ConcurrentHashMap<>();

    public CrossNewYearBagActivity(ActivityInfo activityInfo) {
        reloadConfig(activityInfo);
        reloadData();
    }

    public void reloadConfig(ActivityInfo activityInfo) {
        int activityId = activityInfo.getActivityId();

        Map<String, ActivityConfig> configMap = ActivityBussiness.getActivityConfigMap(Collections.singletonList(activityInfo.getActivityId())).get(activityInfo.getActivityId());

        NewYearBagConfig tmpConfig = new NewYearBagConfig();
        tmpConfig.setActivityInfo(activityInfo);
        tmpConfig.initActivityConfig(configMap);
        tmpConfig.setDrawConfigList(NewYearBagBusiness.getNewYearBagDrawConfigList(activityId));
        tmpConfig.setLimitConfigList(NewYearBagBusiness.getNewYearBagLimitConfigList(activityId));

        config = tmpConfig;

        logger.debug("reload cross new year bag activity id {}.", activityInfo.getActivityId());
        logger.debug("CrossNewYearBagActivity is new code...");
    }

    public void reloadData() {

        int activityId = config.getActivityInfo().getActivityId();

        pool = NewYearBagBusiness.getNewYearBagPool(activityId);
        if (pool == null) {
            pool = new NewYearBagPool();
            pool.setActivityId(activityId);
            pool.setInsertOption();
        }

        userDataMap = NewYearBagBusiness.getNewYearBagUserDataMap(activityId);

        noticeList = NewYearBagBusiness.getNewYearBagNoticeList(activityId, NewYearBagConfig.noticeSize);
        needInsertNoticeList = new ArrayList<>();

        deviceMap = NewYearBagBusiness.getNewYearBagDeviceMap(activityId);

        openUserLimitDataMap = new NewYearBagOpenUserLimitDaoImpl().getNewYearBagOpenUserLimitData(activityId);
    }

    public NewYearBagConfig getConfig() {
        return config;
    }

    public void save() {

        if (pool.isInsertOption()) {
            NewYearBagBusiness.addNewYearBagPool(pool);
        } else if (pool.isUpdateOption()) {
            NewYearBagBusiness.updateNewYearBagPool(pool);
        }

        for (NewYearBagUserData userData : userDataMap.values()) {
            if (userData.isInsertOption()) {
                NewYearBagBusiness.addNewYearBagUserData(userData);
            } else if (userData.isUpdateOption()) {
                NewYearBagBusiness.updateNewYearBagUserData(userData);
            }
        }

        for (NewYearBagDevice device : deviceMap.values()) {
            if (device.isInsertOption()) {
                NewYearBagBusiness.addNewYearBagDevice(device);
            } else if (device.isUpdateOption()) {
                NewYearBagBusiness.updateNewYearBagDevice(device);
            }
        }

        List<NewYearBagNotice> tmpList;
        synchronized (lock) {
            tmpList = new ArrayList<>(needInsertNoticeList);
            needInsertNoticeList.clear();
        }
        for (NewYearBagNotice notice : tmpList) {
            NewYearBagBusiness.addNewYearBagNoticeList(notice);
        }

        for (Map.Entry<String, NewYearBagOpenUserLimit> entry : openUserLimitDataMap.entrySet()) {
            NewYearBagOpenUserLimit openUserLimit = entry.getValue();
            if(openUserLimit.isInsertOption()){
                new NewYearBagOpenUserLimitDaoImpl().add(openUserLimit);
            }else if(openUserLimit.isUpdateOption()){
                new NewYearBagOpenUserLimitDaoImpl().update(openUserLimit);
            }
        }

    }


    // -----------------------------------

    public void openByCmd(long serverId, long userId, NewYearBagProto.CrossNewYearBagOpenReqMsg reqMsg) {
        UserBaseInfo userBaseInfo = PlayerBasePb.parseBaseUserInfo(reqMsg.getPlayerBaseData());
        CrossUserMgr.updateUserBaseInfo(userId, userBaseInfo);

        //openId
        String openId = reqMsg.getOpenId();
        NewYearBagOpenUserLimit openUserLimit = getNewYearBagOpenUserLimit(openId);

        // 设备
        String deviceId = reqMsg.getDeviceId();
        NewYearBagDevice device = getDevice(deviceId);

        // 身份
        int titleId = reqMsg.getPlayerBaseData().getTitleId();

        // 用户群体
        NewYearBagLimitConfig limitConfig = null;
        long vipExp = userBaseInfo.getVipExp();
        for (NewYearBagLimitConfig tmp : config.getLimitConfigList()) {
            if (vipExp >= tmp.getUserCondition()) {
                if (limitConfig == null || limitConfig.getUserLv() < tmp.getUserLv()) {
                    limitConfig = tmp;
                }
            }
        }

        synchronized (lock) {

            // 今天第几天
            long beginDayTime = DateHelper.getZeroTimeStamp(config.getCanOpenTime());
            long todayTime = DateHelper.getZeroTimeStamp(System.currentTimeMillis());
            int day = (int) ((todayTime - beginDayTime) / DateHelper.DAY_MILLIONS) + 1;

            List<String> realRewardList = new ArrayList<>();   //实物奖励
            List<Integer> rewardIdList = new ArrayList<>();    //所有奖励id(配置)
            List<Property> rewardList = new ArrayList<>(); // 奖励一条条记录(不包含实物奖励)
            for (int i = 0; i < reqMsg.getNum(); i++) {

                List<NewYearBagDrawConfig> canDrawList = new ArrayList<>();
                boolean skinLimit = getNumBeforeLimit(limitConfig, day, eNewYearBagType.Skin) <= 0;
                boolean tongBaoLimit = getNumBeforeLimit(limitConfig, day, eNewYearBagType.TongBao) <= 0;
                boolean xianJinLimit = getNumBeforeLimit(limitConfig, day, eNewYearBagType.XianJin) < 6; // 小于6块钱不让抽现金
                boolean deviceXianJinLimit = device.getXianJinTimes() >= config.getDeviceTimes();
                boolean deviceShiWuLimit = device.getShiWuTimes() >= config.getShiWuDeviceTimes();
                boolean openUserXianJinLimit = getNumBeforeByOpenIdUserId(openId, userId, eNewYearBagType.XianJin) >= config.getOpenIdUserIdXianJinLimit();
                boolean openUserShiWuLimit = getNumBeforeByOpenIdUserId(openId, userId, eNewYearBagType.ShiWu) >= config.getOpenIdUserIdShiWuLimit();

                //用于加权重
                List<NewYearBagDrawConfig> shiWuLimitList = new ArrayList<>();

                for (NewYearBagDrawConfig drawConfig : config.getDrawConfigList()) {
                    if (titleId < drawConfig.getTitleLimit()) {
                        continue;
                    }
                    if (drawConfig.getRewardType() == eNewYearBagType.TongBao.getType() && tongBaoLimit) {
                        continue;
                    }
                    if (drawConfig.getRewardType() == eNewYearBagType.XianJin.getType() && (xianJinLimit || deviceXianJinLimit || openUserXianJinLimit)) {
                        continue;
                    }
                    if (drawConfig.getRewardType() == eNewYearBagType.Skin.getType() && skinLimit) {
                        continue;
                    }
                    if (drawConfig.getRewardType() == eNewYearBagType.ShiWu.getType()){

                        if(deviceShiWuLimit || openUserShiWuLimit){
                            continue;
                        }

                        //实物暂时没有分用户区间限制
                        if(vipExp < config.getShiWuVipExpLimit()){
                            continue;
                        }
                        //今日没有开放
                        long dayLimit = drawConfig.getItemLimitMap().getOrDefault(day, 0);
                        if(dayLimit == 0){
                            continue;
                        }
                        NewYearBagShiWuLimitData limitData = pool.getNewYearBagShiWuLimitData(day);
                        List<Integer> itemNumList = StringUtils.stringToIntegerList(drawConfig.getReward(), "=");
                        if(itemNumList.size() != 2){
                            continue;
                        }
                        //今日达到上限
                        long todayNum = limitData.getTodayNum(itemNumList.get(0));
                        if(todayNum >= dayLimit){
                            shiWuLimitList.add(drawConfig);
                            continue;
                        }
                    }
                    canDrawList.add(drawConfig);
                }

                // 拿增加的权重
                Map<Integer, Integer> weightAddiMap = new HashMap<>();
                List<Map<Integer, Integer>> list = new ArrayList<>();
                if (skinLimit) list.add(limitConfig.getSkinChangeWeightMap());
                if (xianJinLimit) list.add(limitConfig.getCashChangeWeightMap());
                if (tongBaoLimit) list.add(limitConfig.getTongBaoChangeWeightMap());

                // 实物权重
                for (NewYearBagDrawConfig drawConfig : shiWuLimitList) {
                    list.add(drawConfig.getChangeWeightMap());
                }

                for (Map<Integer, Integer> map : list) {
                    for (Map.Entry<Integer, Integer> entry : map.entrySet()) {
                        int old = weightAddiMap.getOrDefault(entry.getKey(), 0);
                        weightAddiMap.put(entry.getKey(), entry.getValue() + old);
                    }
                }

                // 拿权重
                List<Integer> weightList = new ArrayList<>();
                for (NewYearBagDrawConfig tmpConfig : canDrawList) {
                    int weight = tmpConfig.getWeightList().get(limitConfig.getUserLv() - 1);
                    weightList.add(weight + weightAddiMap.getOrDefault(tmpConfig.getRewardId(), 0));
                }

                // 随机抽一个
                int index = RandomHelper.getRandomIndexByWeight(weightList, drawRandom);
                NewYearBagDrawConfig drawConfig = canDrawList.get(index);
                Property property = calcRewardAndUpdatePool(limitConfig, day, drawConfig);

                //现金、实物openId  userId限制
                calOpenUserLimit(drawConfig.getRewardType(), openId, userId);

                if(drawConfig.getRewardType() == eNewYearBagType.ShiWu.getType()){
                    realRewardList.add(drawConfig.getReward());
                }else {
                    // 添加奖励
                    rewardList.add(property);
                }

                rewardIdList.add(drawConfig.getRewardId());

                // 上公告
                addNotice(userId, drawConfig, property);

                // 设备限制修改
                if (drawConfig.getRewardType() == eNewYearBagType.XianJin.getType()) {
                    device.setXianJinTimes(device.getXianJinTimes() + 1);
                }else if(drawConfig.getRewardType() == eNewYearBagType.ShiWu.getType()){
                    device.setShiWuTimes(device.getShiWuTimes() + 1);
                }
            }

            // 统计全部奖励
            Property allRewardProp = new Property();
            for (Property property : rewardList) {
                allRewardProp.addProperty(property);
            }
            StringBuilder allRewardStr = new StringBuilder();
            for (Property property : rewardList) {
                allRewardStr.append(PropertyHelper.parsePropertyToString(property));
                allRewardStr.append("#");
            }

            // 记录用户历史奖励
            NewYearBagUserData userData = getUserData(userId);
            Property userReward = StringUtils.isNullOrEmpty(userData.getAllReward()) ? new Property()
                    : PropertyHelper.parseStringToProperty(userData.getAllReward());
            userReward.addProperty(allRewardProp);
            for (String r : realRewardList) {
                userReward.addProperty(PropertyHelper.parseStringToProperty(r));
            }
            userData.setAllReward(PropertyHelper.parsePropertyToString(userReward));

            // 返回
            NewYearBagProto.NewYearBagOpenRespMsg.Builder builder = NewYearBagProto.NewYearBagOpenRespMsg.newBuilder();
            builder.setRet(0);
            builder.setReward(allRewardStr.toString());
            builder.addAllRewardId(rewardIdList);
            builder.addAllRealReward(realRewardList);

            NewYearBagProto.CrossNewYearBagOpenRespMsg.Builder crossRespBuilder = NewYearBagProto.CrossNewYearBagOpenRespMsg.newBuilder();
            crossRespBuilder.setNum(reqMsg.getNum());
            crossRespBuilder.setResp(builder);
            YanQuMessage message = YanQuMessageUtils.buildMessage(GameProtocol.S_NEW_YEAR_BAG_OPEN_FROM_CROSS, crossRespBuilder);
            MessageHelper.sendPacket(serverId, userId, message);
        }
    }

    private void calOpenUserLimit(int rewardType, String openId, long userId) {
        if(rewardType == eNewYearBagType.XianJin.getType()){
            NewYearBagOpenUserLimit openUserLimit = getNewYearBagOpenUserLimit(openId);
            NewYearBagUserTimesData xianJinTimes = openUserLimit.getXianJinTimes();
            xianJinTimes.setUserId(userId);
            xianJinTimes.setTimes(1 + xianJinTimes.getTimes());
            openUserLimit.setUpdateOption();
        }else if(rewardType == eNewYearBagType.ShiWu.getType()){
            NewYearBagOpenUserLimit openUserLimit = getNewYearBagOpenUserLimit(openId);
            NewYearBagUserTimesData shiWuTimes = openUserLimit.getShiWuTimes();
            shiWuTimes.setUserId(userId);
            shiWuTimes.setTimes(1 + shiWuTimes.getTimes());
            openUserLimit.setUpdateOption();
        }
    }

    private int getNumBeforeByOpenIdUserId(String openId, long userId, eNewYearBagType type) {
        NewYearBagOpenUserLimit openUserLimit = getNewYearBagOpenUserLimit(openId);
        if(type == eNewYearBagType.XianJin){
            if(openUserLimit.getXianJinTimes().getUserId() == 0){
                return 0;
            }
            if(openUserLimit.getXianJinTimes().getUserId() != userId){
                //触发过的，其他角色不能触发，直接拉到最大值
                return Integer.MAX_VALUE;
            }else {
                return openUserLimit.getXianJinTimes().getTimes();
            }
        }else if (type == eNewYearBagType.ShiWu){
            if(openUserLimit.getShiWuTimes().getUserId() == 0){
                return 0;
            }
            if(openUserLimit.getShiWuTimes().getUserId() != userId){
                //触发过的，其他角色不能触发，直接拉到最大值
                return Integer.MAX_VALUE;
            }else {
                return openUserLimit.getShiWuTimes().getTimes();
            }
        }
        return Integer.MAX_VALUE;
    }

    public void getRecordByCmd(long serverId, long userId, NewYearBagProto.NewYearBagRecordRespMsg respMsg) {
        NewYearBagProto.NewYearBagRecordRespMsg.Builder builder = respMsg.toBuilder();

        builder.setRet(0);

        NewYearBagUserData userData = getUserData(userId);
        builder.setReward(userData.getAllReward());

        List<NewYearBagNotice> copyList = new ArrayList<>(noticeList);
        for (NewYearBagNotice notice : copyList) {
            builder.addNotice(parseNewYearBagNoticeMsg(notice));
        }

        YanQuMessage message = YanQuMessageUtils.buildMessage(ClientProtocol.U_NEW_YEAR_BAG_RECORD, builder);
        MessageHelper.sendPacket(serverId, userId, message);
    }


    // -----------------------------------

    private NewYearBagUserData getUserData(long userId) {
        synchronized (lock) {
            if (!userDataMap.containsKey(userId)) {
                NewYearBagUserData userData = new NewYearBagUserData();
                userData.setActivityId(config.getActivityInfo().getActivityId());
                userData.setUserId(userId);
                userData.setAllReward("");

                userData.setInsertOption();
                userDataMap.put(userId, userData);
            }
            return userDataMap.get(userId);
        }
    }

    private NewYearBagDevice getDevice(String deviceId) {
        synchronized (lock) {
            if (!deviceMap.containsKey(deviceId)) {
                NewYearBagDevice device = new NewYearBagDevice();
                device.setActivityId(config.getActivityInfo().getActivityId());
                device.setDeviceId(deviceId);
                device.setXianJinTimes(0);
                device.setShiWuTimes(0);

                device.setInsertOption();
                deviceMap.put(deviceId, device);
            }
            return deviceMap.get(deviceId);
        }
    }

    private NewYearBagOpenUserLimit getNewYearBagOpenUserLimit(String openId) {
        synchronized (lock) {
            if (!openUserLimitDataMap.containsKey(openId)) {
                NewYearBagOpenUserLimit openUserLimit = new NewYearBagOpenUserLimit();
                openUserLimit.setActivityId(config.getActivityInfo().getActivityId());
                openUserLimit.setOpenId(openId);
                openUserLimit.setXianJinTimes(new NewYearBagUserTimesData());
                openUserLimit.setShiWuTimes(new NewYearBagUserTimesData());

                openUserLimit.setInsertOption();
                openUserLimitDataMap.put(openId, openUserLimit);
            }
            return openUserLimitDataMap.get(openId);
        }
    }

    private NewYearBagProto.NewYearBagNoticeMsg.Builder parseNewYearBagNoticeMsg(NewYearBagNotice notice) {
        UserBaseInfo userBaseInfo = CrossUserMgr.getUserBaseInfo(notice.getUserId());
        String nickname = userBaseInfo == null ? "" : userBaseInfo.getNickName();

        NewYearBagProto.NewYearBagNoticeMsg.Builder builder = NewYearBagProto.NewYearBagNoticeMsg.newBuilder();
        builder.setNickname(nickname);
        builder.setReward(notice.getReward());
        return builder;
    }

    private long getNumBeforeLimit(NewYearBagLimitConfig limitConfig, int day, eNewYearBagType type) {

        long num = Integer.MAX_VALUE;

        NewYearBagLimitData limitData = pool.getNewYearBagLimitData(limitConfig.getUserLv());

        long allLimit = limitConfig.getAllLimit(type);
        long allNum = limitData.getAllNum(type);
        num = Math.min(allLimit - allNum, num);

        long dayLimit = limitConfig.getSumDayLimit(type, day);
        long todayNum = limitData.getSumTodayNum(type, day);
        num = Math.min(dayLimit - todayNum, num);

        return num;
    }

    private Property calcRewardAndUpdatePool(NewYearBagLimitConfig limitConfig, int day, NewYearBagDrawConfig drawConfig) {

        eNewYearBagType enumType = eNewYearBagType.getTypeByInt(drawConfig.getRewardType());

        // 打开宝箱
        Property tmpReward;
        if (enumType == eNewYearBagType.Ingots || enumType == eNewYearBagType.XianJin || enumType == eNewYearBagType.TongBao) {
            String tmp = drawConfig.getReward();
            int boxId = Integer.valueOf(tmp.substring(0, tmp.indexOf("=")));
            OpenGoodsBagResult result = TempGoodsBagMgr.getRandomGoods(boxId);
            tmpReward = result.getReward();
        } else {
            tmpReward = PropertyHelper.parseStringToProperty(drawConfig.getReward());
        }

        // 只会有一种奖励
        int goodsId = 0;
        BigInteger num = BigInteger.ZERO;
        for (Map.Entry<Integer, BigInteger> entry : tmpReward.getGoods().entrySet()) {
            goodsId = entry.getKey();
            num = entry.getValue();
            break;
        }

        // 最后一抽上限处理
        if (enumType == eNewYearBagType.TongBao) {
            long numBeforeLimit = getNumBeforeLimit(limitConfig, day, enumType);
            if (num.longValue() > numBeforeLimit) {
                num = BigInteger.valueOf(Math.max(numBeforeLimit, 0)); // 通宝最后一抽超出上限 => 取上限
            }
        } else if (enumType == eNewYearBagType.XianJin) {
            long numBeforeLimit = getNumBeforeLimit(limitConfig, day, enumType);
            if (num.longValue() > numBeforeLimit) {
                num = BigInteger.valueOf(Math.min(numBeforeLimit, 6)); // 现金最后一抽超出上限 => 改6块钱（且不超出上限，正常都不会超，除非有bug）

                if (num.longValue() < 6) {
                    logger.error("现金最后一抽不足6块钱还是让他抽了，本抽最终抽到 {} 块钱，活动ID {} 。", num.longValue(), config.getActivityInfo().getActivityId());
                }
            }
        }

        // 修改奖池
        if (enumType == eNewYearBagType.Skin || enumType == eNewYearBagType.XianJin || enumType == eNewYearBagType.TongBao) {
            NewYearBagLimitData data = pool.getNewYearBagLimitData(limitConfig.getUserLv());
            data.addNum(enumType, day, num.longValue());
            pool.setUpdateOption();
        }else if(enumType == eNewYearBagType.ShiWu){
            //实物奖励，没有区分用户vip区间
            NewYearBagShiWuLimitData data = pool.getNewYearBagShiWuLimitData(day);
            data.addNum(goodsId, num.longValue());
            pool.setUpdateOption();
        }

        // 返回最终奖励
        Property property = new Property();
        property.addProperty(goodsId, num);
        return property;
    }

    private void addNotice(long userId, NewYearBagDrawConfig drawConfig, Property property) {
//        eNewYearBagType enumType = eNewYearBagType.getTypeByInt(drawConfig.getRewardType());
//        if (enumType != eNewYearBagType.TongBao && enumType != eNewYearBagType.XianJin && enumType != eNewYearBagType.Skin && enumType != eNewYearBagType.ShiWu) {
//            return;
//        }
        if(drawConfig.getIsBigAward() == 0) {
            return;
        }

        // 容错
        if (property == null || property.isNothing()) {
            return;
        }

        NewYearBagNotice notice = new NewYearBagNotice();
        notice.setActivityId(config.getActivityInfo().getActivityId());
        notice.setUserId(userId);
        notice.setReward(PropertyHelper.parsePropertyToString(property));
        notice.setTime(System.currentTimeMillis());

        synchronized (lock) {
            if (noticeList.size() >= NewYearBagConfig.noticeSize) {
                noticeList.remove(0);
            }
            noticeList.add(notice);

            needInsertNoticeList.add(notice);
        }

        for (long serverId : config.getActivityInfo().getServerIdList()) {
            NewYearBagProto.NewYearSyncNoticeRespMsg.Builder builder = NewYearBagProto.NewYearSyncNoticeRespMsg.newBuilder();
            builder.setNotice(parseNewYearBagNoticeMsg(notice));
            YanQuMessage message = YanQuMessageUtils.buildMessage(AgentProtocol.A_NEW_YEAR_BAG_SYNC_NOTICE, builder);
            MessageHelper.sendPacket(serverId, 0, message);
        }

    }
}
